﻿using System;
using System.Collections.Generic;
using SymbolIntegration.core;
using SymbolIntegration.core.Expressions;

namespace AnalyticIntegration.batch
{
    internal class ThermalConductivityInterpretate
    {
        private const string c_K = "k";
        private const string c_Ki = "ki";
        private const string c_Kj = "kj";


        private const string c_M =  "m";
        private const string c_Mi = "mi";
        private const string c_Mj = "mj";


        private const string c_N = "n";
        private const string c_Ni = "ni";
        private const string c_Nj = "nj";

        public Expression Run(List<string> text, out string sOutError)
        {
            sOutError = "";
            Expression termalExpression = ExpressionFacade.Parse(text[0]);
            KeyValuePair<int, TermalSurfaceData> pair = ReadSurface(text, 1);
            int position = pair.Key;
            TermalSurfaceData mainTermalSurface = pair.Value;
            var otherSurfaces = new List<TermalSurfaceData>();
            int surfaceCount = int.Parse(text[position++]);
            for (int i = 0; i < surfaceCount; i++)
            {
                KeyValuePair<int, TermalSurfaceData> newPair = ReadSurface(text, position);
                otherSurfaces.Add(newPair.Value);
                position = newPair.Key;
            }

            var result = DisideTermalTask(termalExpression, mainTermalSurface, otherSurfaces);
            return result;
        }

        private Expression DisideTermalTask(Expression termalExpression, TermalSurfaceData mainTermalSurface, 
            List<TermalSurfaceData> otherSurfaces)
        {
            var fiExpression = termalExpression.Substitute(new Dictionary<string, Expression>
            {
                {c_K, new VariableExpression(c_Ki)},
                {c_N, new VariableExpression(c_Ni)},
                {c_M, new VariableExpression(c_Mi)},
            });

            var fjExpression = termalExpression.Substitute(new Dictionary<string, Expression>
            {
                {c_K, new VariableExpression(c_Kj)},
                {c_N, new VariableExpression(c_Nj)},
                {c_M, new VariableExpression(c_Mj)},
            });


            Expression result;

            var rootPartOfExpression = ExpressionFacade.DifferentiateByVector(fiExpression,
                mainTermalSurface.NormalVector);
            rootPartOfExpression = rootPartOfExpression * ExpressionFacade.DifferentiateByVector(fjExpression,
                mainTermalSurface.NormalVector);

            result = ExpressionFacade.IntegrateBySurface(mainTermalSurface.GetIntegrateSurfaceData(rootPartOfExpression));

            foreach (var termalSurfaceData in otherSurfaces)
            {
                var surfaceFiDifferential = ExpressionFacade.DifferentiateByVector(fiExpression,
                    termalSurfaceData.NormalVector);
                var surfaceFjDifferential = ExpressionFacade.DifferentiateByVector(fjExpression,
                    termalSurfaceData.NormalVector);

                var surfacePartOfExpression =
                    (surfaceFiDifferential - new VariableExpression("h")*fiExpression)*
                    (surfaceFjDifferential - new VariableExpression("h")*fjExpression);

                surfacePartOfExpression = 
                    ExpressionFacade.IntegrateBySurface(termalSurfaceData.GetIntegrateSurfaceData(surfacePartOfExpression));
                result = result + surfacePartOfExpression;
            }

            return result;
        }

        private KeyValuePair<int, TermalSurfaceData> ReadSurface(List<string> text, int position)
        {
            var surface = new TermalSurfaceData();
            surface.SurfaceExpression = ExpressionFacade.Parse(text[position++]);
            surface.NormalVector.Add("x", ExpressionFacade.Parse(text[position++]));
            surface.NormalVector.Add("y", ExpressionFacade.Parse(text[position++]));
            surface.NormalVector.Add("z", ExpressionFacade.Parse(text[position++]));

            string[] variables = {"x", "y", "z"};
            for (int i = 0; i < variables.Length; i++)
            {
                if (!surface.SurfaceExpression.IsDependsFrom(variables[i]))
                {
                    surface.FunctionVariable = variables[i];
                }
                else
                {
                    surface.OtherVariablesForIntegrateWithBounds.Add(variables[i], new KeyValuePair<Expression, Expression>
                    (ExpressionFacade.Parse(text[position++]), ExpressionFacade.Parse(text[position++])) );
                }
            }
            return new KeyValuePair<int, TermalSurfaceData>(position, surface);
        }
    }
}